package cn.ucox.web.ms.config;

import cn.ucox.web.ms.components.ftp.DexFtpClient;
import cn.ucox.web.ms.components.ftp.FTPClientPool;
import cn.ucox.web.ms.components.ftp.FtpConfig;
import cn.ucox.web.ms.plugin.FastFSFileLoader;
import cn.ucox.web.ms.plugin.OracleDataLoader;
import cn.ucox.web.ms.plugin.PostgresDataLoader;
import cn.ucox.web.ms.polling.BucketDataProcessor;
import com.alibaba.druid.pool.DruidDataSource;
import com.github.tobato.fastdfs.FdfsClientConfig;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.jdbc.core.JdbcTemplate;
import redis.clients.jedis.JedisPoolConfig;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.nio.charset.Charset;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 数据交换配置
 *
 * @author chenw
 * @create 2017-05-10 13:29
 * @email javacspring@gmail.com
 */
@Configuration
//@Import(FdfsClientConfig.class)
public class DataExchangeConfig {

    /*@Bean
    public BucketDataProcessor bucketDataProcessor() {
        return new BucketDataProcessor(pollingConfig(), fastFSFileLoader());
    }*/

    @Bean
    public PollingConfig pollingConfig() {
        return new PollingConfig();
    }

    /*@Bean
    public PersistenceConfig persistenceConfig() {
        return new PersistenceConfig();
    }*/

    /*@Bean
    public PersistenceConfig pollJdbcConfig() {
        return new PersistenceConfig();
    }*/

    @Bean
    public DexRedisConfig pollRedisConfig() {
        return new DexRedisConfig();
    }

    @Bean(name = "exchangeConfig")
    public ExchangeConfig exchangeConfig() {
        return new ExchangeConfig();
    }

    @Bean(name = "ftpConfigHub")
    public FtpConfigHub ftpConfigHub() {
        return new FtpConfigHub();
    }


    //postgresql数据库 数据加载类
    /*@Bean
    public PostgresDataLoader postgresDataLoader(@Qualifier("pollingJdbcTemplate") JdbcTemplate jdbcTemplate) {
        return new PostgresDataLoader(pollingConfig(), jdbcTemplate);
    }*/

    //Oracle数据库 数据加载类
    /*@Bean
    public OracleDataLoader oracleDataLoader(@Qualifier("pollingJdbcTemplate") JdbcTemplate jdbcTemplate) {
        return new OracleDataLoader(pollingConfig(), jdbcTemplate);
    }*/

    /*@Resource
    public FastFileStorageClient storageClient;*/

    /*@Bean
    public FastFSFileLoader fastFSFileLoader() {
        return new FastFSFileLoader(storageClient);
    }*/

    @Bean(name = "pollingJdbcTemplate")
    public JdbcTemplate pollingJdbcTemplate(@Qualifier("pollingDataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
//
//    @Bean(name = "persistenceJdbcTemplate")
//    public JdbcTemplate persistenceJdbcTemplate(@Qualifier("persistenceDataSource") DataSource dataSource) {
//        return new JdbcTemplate(dataSource);
//    }

    @Bean(value = "pollJedisConnFactory")
    public JedisConnectionFactory jedisConnectionFactory() {

        DexRedisConfig redisConfig = pollRedisConfig();

        //JedisConnectionFaction
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName(redisConfig.getHost());
        factory.setPort(redisConfig.getPort());
        factory.setPassword(redisConfig.getPassword());
        factory.setUsePool(redisConfig.isUsePool());

        //JedisPollConfig
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(redisConfig.getMaxIdle());
        jedisPoolConfig.setMaxWaitMillis(redisConfig.getMaxWaitMillis());
        jedisPoolConfig.setMaxTotal(redisConfig.getMaxTotal());
        jedisPoolConfig.setTestOnBorrow(redisConfig.isTestOnBorrow());

        factory.setPoolConfig(jedisPoolConfig);

        return factory;
    }

    @Bean(value = "pollRedisTemplate")
    public RedisTemplate<String, Object> redisTemplate() {

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());

        //String Serializer
        RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer(Charset.forName("UTF-8"));
        redisTemplate.setStringSerializer(stringRedisSerializer);
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);

        //JSON Serializer
//        RedisJsonSerializer redisJsonSerializer = new RedisJsonSerializer();
        redisTemplate.setValueSerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(stringRedisSerializer);

        //Default Serializer
        redisTemplate.setDefaultSerializer(stringRedisSerializer);
        redisTemplate.setEnableDefaultSerializer(true);

        return redisTemplate;
    }

    @Primary
    @Bean(name = "pollingDataSource", destroyMethod = "close")
    public DataSource pollingDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        PollingConfig pollingConfig = pollingConfig();
        dataSource.setDriverClassName(pollingConfig.getDriver());
        dataSource.setUrl(pollingConfig.getUrl());
        dataSource.setUsername(pollingConfig.getUser());
        dataSource.setPassword(pollingConfig.getPassword());
        dataSource.setInitialSize(pollingConfig.getInitialSize());
        dataSource.setMinIdle(pollingConfig.getMinIdle());
        dataSource.setMaxActive(pollingConfig.getMaxActive());
        dataSource.setMaxWait((long) pollingConfig.getMaxWait());
//        if (pollingConfig.getDriver().equalsIgnoreCase("oracle.jdbc.driver.OracleDriver")) {
//            dataSource.setPoolPreparedStatements(true);
//        } else {
//            dataSource.setPoolPreparedStatements(false);
//        }

        try {
            dataSource.setFilters("stat");
            return dataSource;
        } catch (SQLException var4) {
            throw new RuntimeException("Configuration Wallfilter failure", var4);
        }
    }

    /*@Bean(name = "persistenceDataSource", destroyMethod = "close")
    public DataSource persistenceDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        PersistenceConfig persistenceConfig = persistenceConfig();
        dataSource.setDriverClassName(persistenceConfig.getDriver());
        dataSource.setUrl(persistenceConfig.getUrl());
        dataSource.setUsername(persistenceConfig.getUser());
        dataSource.setPassword(persistenceConfig.getPassword());
        dataSource.setInitialSize(persistenceConfig.getInitialSize());
        dataSource.setMinIdle(persistenceConfig.getMinIdle());
        dataSource.setMaxActive(persistenceConfig.getMaxActive());
        dataSource.setMaxWait((long) persistenceConfig.getMaxWait());
        if (persistenceConfig.getDriver().equalsIgnoreCase("oracle.jdbc.driver.OracleDriver")) {
            dataSource.setPoolPreparedStatements(true);
        } else {
            dataSource.setPoolPreparedStatements(false);
        }

        try {
            dataSource.setFilters("wall,stat");
            return dataSource;
        } catch (SQLException var4) {
            throw new RuntimeException("Configuration Wallfilter failure", var4);
        }
    }*/

    @ConfigurationProperties(prefix = "dex.exchange")
    public static class ExchangeConfig {

        private static final Logger logger = LoggerFactory.getLogger(ExchangeConfig.class);
        //发送数据分片大小为500K
        private int segmentSize = 512000;
        //发送数据备份目录
        private String sendBackup;
        private String sendError;
        //接收事务数据备份目录
        private String receiveBackup;
        //接收错误事务数据备份目录
        private String receiveError;
        /**
         * 导入数据表
         */
        private String importTables;
        /**
         * 导出数据表
         */
        private String exportTables;

        private String attachMapper;

        public int getSegmentSize() {
            return segmentSize;
        }

        public void setSegmentSize(int segmentSize) {
            this.segmentSize = segmentSize;
        }

        public String getSendBackup() {
            return sendBackup;
        }

        public void setSendBackup(String sendBackup) {
            this.sendBackup = sendBackup;
        }

        public String getReceiveBackup() {
            return receiveBackup;
        }

        public void setReceiveBackup(String receiveBackup) {
            this.receiveBackup = receiveBackup;
        }

        public String getSendError() {
            return sendError;
        }

        public void setSendError(String sendError) {
            this.sendError = sendError;
        }

        public String getReceiveError() {
            return receiveError;
        }

        public void setReceiveError(String receiveError) {
            this.receiveError = receiveError;
        }

        public void setImportTables(String importTables) {
            this.importTables = importTables;
        }

        public void setExportTables(String exportTables) {
            this.exportTables = exportTables;
        }

        public String[] getImportTables() {
            if (null != this.importTables) {
                return this.importTables.split(",");
            }
            return new String[0];
        }

        public String[] getExportTables() {
            if (null != this.exportTables) {
                return this.exportTables.split(",");
            }
            return new String[0];
        }

        public Map<String, String> getAttachmentColumnMapper() {
            String[] rows = this.attachMapper.split(",");
            HashMap<String, String> result = new HashMap<>(rows.length);
            for (String row : rows) {
                String[] kv = row.split(":");
                if (kv.length != 2) {
                    logger.error("附件映射配置错误");
                    continue;
                }
                result.put(kv[0], kv[1]);
            }
            return result;
        }

        public String getAttachMapper() {
            return attachMapper;
        }

        public void setAttachMapper(String attachMapper) {
            this.attachMapper = attachMapper;
        }
    }

    @ConfigurationProperties(prefix = "dex.redis")
    public static class DexRedisConfig extends ExchangeConfig {

        private String host;
        private int port = 6379;
        private String password;
        private int dbIndex = 0;
        private boolean usePool = true;

        private int maxIdle = 1000;
        private int minIdle = 100;
        private long maxWaitMillis = 3000;
        private int maxTotal = 5000;
        private int timeout = 2000;
        private boolean testOnBorrow = true;

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        public int getTimeout() {
            return timeout;
        }

        public void setTimeout(int timeout) {
            this.timeout = timeout;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public int getDbIndex() {
            return dbIndex;
        }

        public void setDbIndex(int dbIndex) {
            this.dbIndex = dbIndex;
        }

        public boolean isUsePool() {
            return usePool;
        }

        public void setUsePool(boolean usePool) {
            this.usePool = usePool;
        }

        public int getMaxIdle() {
            return maxIdle;
        }

        public void setMaxIdle(int maxIdle) {
            this.maxIdle = maxIdle;
        }

        public int getMinIdle() {
            return minIdle;
        }

        public void setMinIdle(int minIdle) {
            this.minIdle = minIdle;
        }

        public long getMaxWaitMillis() {
            return maxWaitMillis;
        }

        public void setMaxWaitMillis(long maxWaitMillis) {
            this.maxWaitMillis = maxWaitMillis;
        }

        public int getMaxTotal() {
            return maxTotal;
        }

        public void setMaxTotal(int maxTotal) {
            this.maxTotal = maxTotal;
        }

        public boolean isTestOnBorrow() {
            return testOnBorrow;
        }

        public void setTestOnBorrow(boolean testOnBorrow) {
            this.testOnBorrow = testOnBorrow;
        }
    }

    @ConfigurationProperties(prefix = "dex.polling")
    public static class PollingConfig {

        private String dbType;
        private String driver;
        private String url;
        private String user;
        private String password;

        private int initialSize = 5;
        private int minIdle = 2;
        private int maxActive = 100;
        private int maxWait = 500;

        private long interval = 2000;//轮询间隔时间(ms)
        private long minInterval = 1000;//最短轮询时间间隔，自动调频后达到最小值，轮询频率为最小值
        private long maxInterval = 10000;//最长轮询时间间隔，自动调频后达到最大值时，轮询频率为最大值
        private long intervalOffset = 200;//轮询智能调频参数，默认200ms，即如果长时间没有同步数据，则轮询频率将根据设定调频参数逐步延长轮询时间
        private long processNumPerCpuThread = 50;//每CPU线程处理数据条数，如设置为50，则在四核8线程CPU下一次同步可同时处理400条数据
        private int maxFtpDetention = 0;

        public String getDbType() {
            return dbType;
        }

        public void setDbType(String dbType) {
            this.dbType = dbType;
        }

        public long getInterval() {
            return interval;
        }

        public void setInterval(long interval) {
            this.interval = interval;
        }

        public long getMinInterval() {
            return minInterval;
        }

        public void setMinInterval(long minInterval) {
            this.minInterval = minInterval;
        }

        public long getMaxInterval() {
            return maxInterval;
        }

        public void setMaxInterval(long maxInterval) {
            this.maxInterval = maxInterval;
        }

        public long getIntervalOffset() {
            return intervalOffset;
        }

        public void setIntervalOffset(long intervalOffset) {
            this.intervalOffset = intervalOffset;
        }

        public long getProcessNumPerCpuThread() {
            return processNumPerCpuThread;
        }

        public void setProcessNumPerCpuThread(long processNumPerCpuThread) {
            this.processNumPerCpuThread = processNumPerCpuThread;
        }

        public String getDriver() {
            return driver;
        }

        public void setDriver(String driver) {
            this.driver = driver;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getUser() {
            return user;
        }

        public void setUser(String user) {
            this.user = user;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public int getInitialSize() {
            return initialSize;
        }

        public void setInitialSize(int initialSize) {
            this.initialSize = initialSize;
        }

        public int getMinIdle() {
            return minIdle;
        }

        public void setMinIdle(int minIdle) {
            this.minIdle = minIdle;
        }

        public int getMaxActive() {
            return maxActive;
        }

        public void setMaxActive(int maxActive) {
            this.maxActive = maxActive;
        }

        public int getMaxWait() {
            return maxWait;
        }

        public void setMaxWait(int maxWait) {
            this.maxWait = maxWait;
        }

        public int getMaxFtpDetention() {
            return maxFtpDetention;
        }

        public void setMaxFtpDetention(int maxFtpDetention) {
            this.maxFtpDetention = maxFtpDetention;
        }
    }

    @ConfigurationProperties(prefix = "dex.persistence")
    public static class PersistenceConfig {

        private String dbType;
        private String driver;
        private String url;
        private String user;
        private String password;

        private int initialSize = 5;
        private int minIdle = 2;
        private int maxActive = 100;
        private int maxWait = 500;

        private int heartbeatInterval = 5;
        private int heartbeatCheckIp = 5;

        public String getDbType() {
            return dbType;
        }

        public void setDbType(String dbType) {
            this.dbType = dbType;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getUser() {
            return user;
        }

        public void setUser(String user) {
            this.user = user;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getDriver() {
            return driver;
        }

        public void setDriver(String driver) {
            this.driver = driver;
        }

        public int getInitialSize() {
            return initialSize;
        }

        public void setInitialSize(int initialSize) {
            this.initialSize = initialSize;
        }

        public int getMinIdle() {
            return minIdle;
        }

        public void setMinIdle(int minIdle) {
            this.minIdle = minIdle;
        }

        public int getMaxActive() {
            return maxActive;
        }

        public void setMaxActive(int maxActive) {
            this.maxActive = maxActive;
        }

        public int getMaxWait() {
            return maxWait;
        }

        public void setMaxWait(int maxWait) {
            this.maxWait = maxWait;
        }

        public int getHeartbeatInterval() {
            return heartbeatInterval;
        }

        public void setHeartbeatInterval(int heartbeatInterval) {
            this.heartbeatInterval = heartbeatInterval;
        }

        public int getHeartbeatCheckIp() {
            return heartbeatCheckIp;
        }

        public void setHeartbeatCheckIp(int heartbeatCheckIp) {
            this.heartbeatCheckIp = heartbeatCheckIp;
        }
    }

    @ConfigurationProperties(prefix = "dex.ftp")
    public static class FtpConfigHub {

        private List<FtpConfig> accounts = new ArrayList<>();

        private Map<String, FtpConfig> members;

        public List<FtpConfig> getAccounts() {
            return accounts;
        }

        public void setAccounts(List<FtpConfig> accounts) {
            this.accounts = accounts;
        }

        public FtpConfig getFtpConfig(String accountName) {
            if (null == this.members)
                this.members = accounts.stream().collect(Collectors.toMap(FtpConfig::getUserName, item -> item));
            return this.members.get(accountName);
        }
    }
}
